﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LightGridData
{

    public const int UNKNOWN_OCCLUSION = -1;

    public bool mOpaque = false;
    public Color mColor;
    public int mX;
    public int mY;

    public int mTouched = 0;
    public double mOcclusion = UNKNOWN_OCCLUSION;
    private Sector mLightSector = new Sector();
    private Sector mInComingA = new Sector();
    private Sector mInComingB = new Sector();

    public LightGridData mLeft;
    public LightGridData mRight;
    public LightGridData mUp;
    public LightGridData mDown;

    public LightGridData(int cx, int cy)
    {
        mX = cx;
        mY = cy;
    }

    public int GetColor
    {
        get
        {
            if (mOpaque)
                return 0x7080C0;
            else
                return mColor.GetHashCode();
        }
    }

    public void Clear(int c)
    {
        mColor.r = (c >> 16 & 0xFF) / 255;
        mColor.g = (c >> 8 & 0xFF) / 255;
        mColor.b = (c & 0xFF) / 255;
    }

    public void AddScaled(int c, double sf)
    {
        mColor.r += (float)(sf * (c >> 16 & 0xFF) / 255.0f);
        mColor.g += (float)(sf * (c >> 8 & 0xFF) / 255.0f);
        mColor.b += (float)(sf * (c & 0xFF) / 255.0f);
    }

    public void ResetOcclusion()
    {
        mOcclusion = UNKNOWN_OCCLUSION;
    }


    public void CalcOcclusion(Vector2 pos, bool normalize)
    {
        if (mOpaque)
        {
            mLightSector.Clear();
            mOcclusion = 1.0;
            return;
        }


        //vector (dx, dy) describes relative position from center
        int dx = (int)Math.Ceiling(pos.x) - mX - 1;
        int dy = (int)Math.Ceiling(pos.y) - mY - 1;
        //sign of vector components(矢量)
        int sx = (dx == 0) ? 0 : (dx < 0) ? -1 : 1;
        int sy = (dy == 0) ? 0 : (dy < 0) ? -1 : 1;
        //offset necessary for vertical/horizontal lines
        int ox = (sy == 0) ? sx : 0;
        int oy = (sx == 0) ? sy : 0;
        //calculate the points that define the sector
        double x1 = mX + 0.5 * (1 - sy + ox);
        double y1 = mY + 0.5 * (1 + sx + oy);
        double x2 = mX + 0.5 * (1 + sy + ox);
        double y2 = mY + 0.5 * (1 - sx + oy);


        //calculate sector
        mLightSector.SetFromCoords(pos.x, pos.y, x1, y1, x2, y2, normalize);

        if (Math.Abs(dx) + Math.Abs(dy) > 1) //no direct connection with lightsource - there might be occlusion
        {
            //gather horizontal
            if (dx > 0 && mRight != null)
                mInComingA.SetIntersection(mLightSector, mRight.mLightSector);
            else if (dx < 0 && mLeft != null)
                mInComingA.SetIntersection(mLightSector, mLeft.mLightSector);
            else
                mInComingA.Clear();

            //gather vertical
            if (dy > 0 && mDown != null)
                mInComingB.SetIntersection(mLightSector, mDown.mLightSector);
            else if (dy < 0 && mUp != null)
                mInComingB.SetIntersection(mLightSector, mUp.mLightSector);
            else
                mInComingB.Clear();

            //combine exposure from both edges and compare with max possible exposure (myTheta)
            double myTheta = mLightSector.mTheta;
            mLightSector.SetUnion(mInComingA, mInComingB);
            mOcclusion = 1.0 - (mLightSector.mTheta / myTheta);
        }
        else
            mOcclusion = 0.0; //ends recursion			
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="lightPos">光源位置</param>
    /// <param name="normalize"></param>
    public void CalclOcclusionRecursive(Vector2 lightPos, bool normalize)
    {
        if (mOcclusion != UNKNOWN_OCCLUSION)
            return;

        //offset relative to the center position
        int dx = (int)Math.Ceiling(lightPos.x) - mX - 1;
        int dy = (int)Math.Ceiling(lightPos.y) - mY - 1;

        //make sure relevant neighbours are evaluated first
        LightGridData a = null;
        if (dx > 0)
            a = mRight;
        else if (dx < 0)
            a = mLeft;

        if (a != null)
            a.CalclOcclusionRecursive(lightPos, normalize);

        LightGridData b = null;
        if (dy > 0)
            b = mDown;
        else if (dy < 0)
            b = mUp;

        if (b != null)
            b.CalclOcclusionRecursive(lightPos, normalize);

        //the rest is similar to the iterator version...

        if (mOpaque)
        {
            mLightSector.Clear();
            mOcclusion = 1.0;
            return;
        }

        //方向
        double sx = (dx == 0) ? 0 : (dx < 0) ? -1 : 1;          
        double sy = (dy == 0) ? 0 : (dy < 0) ? -1 : 1;

        //offset necessary for vertical/horizontal lines
        double ox = (sy == 0) ? sx : 0;
        double oy = (sx == 0) ? sy : 0;

        //calculate the points that define the sector
        double x1 = mX + 0.5 * (1 - sy + ox);
        double y1 = mY + 0.5 * (1 + sx + oy);
        double x2 = mX + 0.5 * (1 + sy + ox);
        double y2 = mY + 0.5 * (1 - sx + oy);

        mLightSector.SetFromCoords(lightPos.x, lightPos.y, x1, y1, x2, y2, normalize);

        if (Math.Abs(dx) + Math.Abs(dy) > 1) //no direct connection with lightsource - there might be occlusion(没有直接连接光源——可能会阻塞)    
        {
            //evaluate right
            if (a != null)
                mInComingA.SetIntersection(mLightSector, a.mLightSector);
            else
                mInComingA.Clear();

            //evaluate down
            if (b != null)
                mInComingB.SetIntersection(mLightSector, b.mLightSector);
            else
                mInComingB.Clear();

            //combine exposure from both edges and compare with max possible exposure (myTheta)
            double myTheta = mLightSector.mTheta;
            mLightSector.SetUnion(mInComingA, mInComingB);
            mOcclusion = 1.0 - (mLightSector.mTheta / myTheta);
        }
        else
            mOcclusion = 0.0; //ends recursion				
    }






}
